package com.haruhi.utils;

import com.haruhi.entity.FileDetail;
import com.haruhi.entity.vo.UploadJsonObj;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.UUID;

@Component
public class FileUtil {

    private static final Logger LOG = LoggerFactory.getLogger(FileUtil.class);

    @Value("${fileConfig.charset}")
    private String utf8;

    @Value("${fileConfig.filePath}")
    private String filePath;

    /**
     * 分片上传文件
     *
     * @param response
     * @param file     这只是一个分片
     * @param body     上传时的文件信息（分片信息以及文件名，文件大小）
     * @return 返回实体，只set了部分属性
     */
    public FileDetail partUpload(HttpServletResponse response, MultipartFile file, UploadJsonObj body) {
        response.setCharacterEncoding(utf8);

        File f = new File(filePath);
        if (!f.exists()) {
            f.mkdirs();
        }
        // 当前分片数
        Integer schunk = null;
        // 总分片数
        Integer schunks = null;
        // 文件名
        String name = null;
        if (null != body) {
            schunk = body.getChunk();
            schunks = body.getChunks();
            name = body.getName();
        }
        BufferedOutputStream bos = null;
        try {
            DiskFileItemFactory factory = new DiskFileItemFactory();
            // 缓冲区:先读到内存
            factory.setSizeThreshold(4096);
            factory.setRepository(new File(filePath));
            ServletFileUpload upload = new ServletFileUpload(factory);
            // 限制单个文件大小
            upload.setFileSizeMax(1L * 1024L * 1024L * 1024L);
            // 限制总大小
            upload.setSizeMax(2L * 1024L * 1024L * 1024L);
            String temFileName = name;
            if (null != name) {
                if (null != schunk) {
                    temFileName = schunk + "_" + name;
                }
                File temFile = new File(filePath, temFileName);
                // 当这个分片不存在于服务器的时候才写入
                if (!temFile.exists()) {
                    file.transferTo(temFile);
                }

            }
            File temFile = null;
            FileDetail result = null;
            // 当这个分片是最后一个分片时进行合并
            if (null != schunk && schunk.intValue() == schunks.intValue() - 1) {
                String uuid = getUUid();
                String type = getSuffix(name);
                String newName = uuid;
                if (!ParamUtil.isEmpty(type)) {
                    newName = uuid + "." + type;
                }

                temFile = new File(filePath, newName);

                bos = new BufferedOutputStream(new FileOutputStream(temFile));
                // 合并需要遍历所有分片
                for (int i = 0; i < schunks; i++) {
                    File fileItem = new File(filePath, i + "_" + name);
                    while (!fileItem.exists()) {
                        Thread.sleep(500);
                    }
                    byte[] bytes = FileUtils.readFileToByteArray(fileItem);
                    bos.write(bytes);
                    bos.flush();
                    fileItem.delete();
                }
                LOG.info("合并了一个文件,全路径==> {}",temFile);
                bos.flush();

                result = new FileDetail();
//                result.setFile(temFile);
                // 这是文件原名称（可能重复）
//                result.setName(name);
                // uuid（无后缀）
                result.setUuid(uuid);
                // 文件全路径
                result.setRealName(temFile.toString());
                // 后缀
                result.setType(type);
                result.setMd5(getMD5(temFile));

                result.setSize((float) body.getSize() / 1024f / 1024f);
                result.setEnabled(true);
            }

            return result;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != bos) {
                    bos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 删除文件
     * @param path
     */
    @Async
    public void deleteFile(String path){
        File file = new File(path);
        deleteFile(file);
    }
    /**
     * 删除文件
     * @param file
     */
    @Async
    public void deleteFile(File file){
        if(file.exists()){
            file.delete();
        }
    }

    /**
     * 获取文件新的名称
     * 包含后缀
     *
     * @param originFileName
     * @return
     */
    private String getNewName(String originFileName) {
        return getUUid() + "." + getSuffix(originFileName);
    }

    /**
     * 获取uuid
     *
     * @return
     */
    private String getUUid() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    /**
     * 获取文件后缀
     * @param fileName
     * @return
     */
    private String getSuffix(String fileName) {
        try {
            String[] split = fileName.split("\\.");

            return split[split.length - 1];
        }catch (ArrayIndexOutOfBoundsException e){
            return null;
        }catch (NullPointerException e){
            return null;
        }

    }

    /**
     * 获取md5
     * 这里也需要关闭输入流
     * 否则文件会一直处于被占用中
     * @param file
     * @return
     */
    public String getMD5(File file) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
            return DigestUtils.md5DigestAsHex(fis);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != fis) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }
    /**
     * 获取md5
     * 这里也需要关闭输入流
     * 否则文件会一直处于被占用中
     * @param file
     * @return
     */
    public String getMD5(MultipartFile file) {
        InputStream is = null;
        try {
            is = file.getInputStream();
            return DigestUtils.md5DigestAsHex(is);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }


}
